package org.example.groupchat;

import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.util.concurrent.GlobalEventExecutor;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * @author xuan
 * @create 2023/9/14
 */
public class GroupChatServerHandler extends SimpleChannelInboundHandler<String> {
    /**
     * 定义一个channel组，管理所有的channel
     * GlobalEventExecutor.INSTANCE 是全局的事件执行器，是一个单例
     * */
    private static ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
    /**
     * 定义一个时间的输出格式
     */
    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    /**
     * 当连接建立之后，第一个被执行
     * 一连接成功，就把当前的 Channel 加入到 ChannelGroup，并将上线消息推送给其他客户
     */
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        // 获取当前 Channel
        Channel channel = ctx.channel();
        Date date = new Date(System.currentTimeMillis());
        //将该客户加入聊天的信息推送给其它在线的客户端
        //该方法会将channelGroup中所有的channel遍历，并发送消息，因此不需要我们遍历channelGroup中的所有channel
        channelGroup.writeAndFlush("[客户端] ["+dateFormat.format(date)+"] "+channel.remoteAddress()+" 加入群聊~");
        // 将当前 Channel 加入 ChannelGroup
        channelGroup.add(channel);
    }

    /**
     * 表示channel处于活动状态，可以提示 XXX 上线
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        Date date = new Date(System.currentTimeMillis());
        System.out.println("["+dateFormat.format(date)+"] "+ctx.channel().remoteAddress() + " 已上线~");
    }

    /**
     * 当断开连接之后，将 XXX 退出群聊消息推送给当前在线的客户
     * 当某个 Channel 执行到这个方法，会自动从 ChannelGroup 中移除
     */
    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        Date date = new Date(System.currentTimeMillis());
        channelGroup.writeAndFlush("[客户端] ["+dateFormat.format(date)+"] "+ctx.channel().remoteAddress() + " 退出群聊~");
        // 输出 ChannelGroup 的大小
        System.out.println("==== ChannelGroup-Size : " + channelGroup.size());
    }


    /**
     * 当 Channel 处于不活动的状态激活，提示 XXX 离线
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        Date date = new Date(System.currentTimeMillis());
        System.out.println("["+dateFormat.format(date)+"] "+ctx.channel().remoteAddress() + " 已下线~");
    }

    /**
     * 读取数据，并把读取到的数据转发给所有 客户
     */
    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, String msg) throws Exception {
        // 获取到 当前 Channel
        Channel channel = channelHandlerContext.channel();

        Date date = new Date(System.currentTimeMillis());

        //遍历 ChannelGroup 根据不同的情况，推送不同的消息
        channelGroup.forEach(ch -> {
            if (ch != channel) {//遍历到的当前的 ch 不是发消息的 Channel
                ch.writeAndFlush("[客户端] [" + dateFormat.format(date) + "] " + channel.remoteAddress() + " 发送了消息 ：" + msg );
            } else {
                //回显自己发送的消息给自己
                ch.writeAndFlush("[自己]发送了消息 [" + dateFormat.format(date) + "] " + msg);
            }
        });
    }

    /**
     * 异常处理
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        // 关闭该通道
        ctx.close();
    }
}
